#include "nd_app/nd_app.h"
#include "nd_common/nd_rbtree.h"
#include "nd_quadtree/quadtree.h"
//#include "nd_listensrv.h"
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include "OEM/map.h"
#include "demomsg.h"
#include "nd_log4c.h"

//#define LISTEN_PORT  (12346)
//#define MAX_CLIENT   (32)
/*
 * manage many types of gate server,
 */ 
#define MAX_QUAD_SEARCH (100)
#define MAX_CLEAN_USER  (100)
static log4c_category_t* mycat = NULL;	

//todo need lock
static nd_mutex map_lock;
static quadtree_t *mapobj_tree = NULL;   //find user by position

static nd_rwlock name_map_lock;
static rb_node_t *username_map = NULL;   //find user by rolename
static nd_rwlock id_map_lock;
static rb_node_t *userid_map = NULL;     //find user by id

static NDINT32 init_mapboj_tree()
{
	quadbox_t box = {0, 0, 20, 16};
	mapobj_tree = quadtree_create(box, QTREE_DEPTH_MIN, QBOX_OVERLAP_MIN);
	nd_mutex_init(&map_lock);
	return (0);
}

static MAP maps[20][16];
static NDINT32 init_map(char *filename)
{
	FILE *fp=fopen(filename,"r+");
	fread(maps,sizeof(MAP),320,fp);
	fclose(fp);
	return (0);
}

static lua_State *L = NULL;
static int reloadconfig(lua_State *l)
{
	printf("%s\n", __FUNCTION__);
	return (0);
}
static int lua_addnpc(lua_State *l)
{
	printf("%s\n", __FUNCTION__);
	return (0);
}

static int lua_init(char *filename)
{
	L = lua_open();
    luaopen_base(L);
//    luaopen_io(L);
    luaopen_string(L);
    luaopen_math(L);

	if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0)) {
		error(L, "cannot run configuration file: %s", lua_tostring(L, -1));
		return (-1);
	}
	lua_pushcfunction(L, reloadconfig);
	lua_setglobal(L, "reload");
	lua_pushcfunction(L, lua_addnpc);
	lua_setglobal(L, "addnpc");
	
	return (0);
}

static unsigned short get_listenport()
{
	unsigned short listen_port = 0;
	lua_getglobal(L, "listen_port");
    if (!lua_isnumber(L, -1)) {
       error(L, "listen_port should be a number\n");
	   return (0);
	}
	listen_port = lua_tonumber(L, -1);
	return listen_port;
}
static unsigned short get_maxclient()
{
	int max_client = 0;
	lua_getglobal(L, "max_client");
    if (!lua_isnumber(L, -1)) {
       error(L, "max_client should be a number\n");
	   return (0);
	}
	max_client = lua_tonumber(L, -1);
	return max_client;
}

#define MAX_GATE_SERVER (10)
static NDUINT16 gate_server_id[MAX_GATE_SERVER];

static NDINT32 test_accept(NDUINT16 sessionid, SOCKADDR_IN *addr, nd_handle listener)
{
	NDINT32 i;
    printf("%s %d: sessonid[%hx] addr[%s] listener[]\n", __FUNCTION__, __LINE__, sessionid, (char *)(inet_ntoa(addr->sin_addr)));
	for (i = 0; i < MAX_GATE_SERVER; ++i) {
		if (gate_server_id[i] == (NDUINT16)-1) {
			gate_server_id[i] = sessionid;
			break;
		}
	}

	if (i >= MAX_GATE_SERVER) {
		LOG_ERROR(mycat, "gate server too much");
		return (-1);
	}

    return (0);
}

static void test_deaccept(NDUINT16 sessionid, nd_handle listener)
{
	NDINT32 i;
	printf("%s %d: sessonid[%hx] listener[]\n", __FUNCTION__, __LINE__, sessionid);
	for (i = 0; i < MAX_GATE_SERVER; ++i) {
		if (gate_server_id[i] == sessionid) {
			gate_server_id[i] = -1;
			break;
		}
	}
	if (i >= MAX_GATE_SERVER) {
		LOG_ERROR(mycat, "can not find sessionid when deacdept");
	}
}

static NDINT32 clean_user(unsigned long id)
{
	user_info_t *user_info;
	rb_node_t *usernode;

	nd_rdlock(id_map_lock);
	usernode = rb_search(id, userid_map);
	nd_rdunlock(id_map_lock);
	if (!usernode || !usernode->data) {
		LOG_ERROR(mycat, "%s %d: can not find usernode[%d]", __FUNC__, __LINE__, id);					
		return (-1);
	}
	user_info = usernode->data;

	nd_wrlock(id_map_lock);
	userid_map = rb_erase(id, userid_map);
	nd_wrunlock(id_map_lock);

	nd_wrlock(name_map_lock);	
	username_map = rb_erase_v2(user_info->rolename, username_map);
	nd_wrlock(name_map_lock);
	
	nd_mutex_lock(map_lock);
	list_del(&user_info->quad_lst);
	nd_mutex_unlock(map_lock);
	
	free(user_info);
	return (0);
}

//todo clean not active user
static NDINT32 tryto_clean_user()
{
	NDUINT32 clean_id[MAX_CLEAN_USER];
	return (0);
}

static NDINT32 test_usermsg_func(NDUINT16 sessionid, nd_usermsgbuf_t *msg , nd_handle listener)
{
	printf("%s %d: sessonid[%hx] listener[]\n", __FUNCTION__, __LINE__, sessionid);
}

static NDINT32 server_reg_func(NDUINT16 sessionid, nd_usermsgbuf_t *msg , nd_handle h_listen)
{
	return (0);
}

static NDINT32 user_leave_map(NDUINT16 sessionid, nd_usermsgbuf_t *msg , nd_handle listener)
{
	NDUINT32 id;
	nd_assert(ND_USERMSG_MAXID(msg) == MAXID_SERVER_SERVER);
	nd_assert(ND_USERMSG_MINID(msg) == MSG_USER_LEAVE_MAP);
	id = ND_USERMSG_PARAM(msg);
	clean_user(id);
	return (0);
}

//todo 
static NDINT32 moveto_newmap(NDINT32 id_newmap, user_info_t *user_info, struct nd_client_map *session)
{
	nd_usermsgbuf_t buf;
	NDINT32 ret = -1;
	moveto_newmap_t *data = (moveto_newmap_t *)buf.data;

	data->id = id_newmap;
	memcpy(&data->user_info, user_info, sizeof(user_info_t));
	ND_USERMSG_MAXID(&buf) = MAXID_SERVER_SERVER ;
	ND_USERMSG_MINID(&buf) = MSG_USER_LEAVE_MAP;
	ND_USERMSG_LEN(&buf) = ND_USERMSG_HDRLEN + sizeof(moveto_newmap_t);
	ND_USERMSG_PARAM(&buf) = user_info->id;	
	ND_USERMSG_ENCRYPT(&buf) = 0;
	ret = nd_sessionmsg_sendex((nd_handle)session, (nd_usermsghdr_t *)&buf, ESF_URGENCY);
	if (ret == ND_USERMSG_LEN(&buf)) {
			//maybe we should wait gateserver return and then clean_user
			//clean_user(id);
		return (0);
	}
	LOG_ERROR(mycat, "%s %d: sendex ret %d", __FUNC__, __LINE__, ret);
	return (-1);
}

static NDINT32 user_change_map(NDUINT16 sessionid, nd_usermsgbuf_t *msg , nd_handle listener)
{
	login_ack_t *data = (login_ack_t *)msg->data;					
	user_info_t *user = NULL;
//	nd_usermsgbuf_t buf = ND_USERMSG_INITILIZER;
	struct nd_client_map *session = NULL;
	NDINT32 ret = -1;
		//long key;

	struct cm_manager *mamager = nd_listensrv_get_cmmamager(listener);
	
	nd_assert(mamager) ;

	session = (struct nd_client_map *)mamager->lock(mamager, sessionid) ;
	if(session) {
		if(nd_connector_valid((nd_netui_handle) session) && sessionid == session->connect_node.session_id) {
			assert(ND_USERMSG_MAXID(msg) == MAXID_SERVER_SERVER);
			assert(ND_USERMSG_MINID(msg) == MSG_USER_CHG_MAP);
			assert(ND_USERMSG_DATALEN(msg) == sizeof(login_ack_t));

			user = malloc(sizeof(user_info_t));
			if (!user) {
				LOG_ERROR(mycat, "%s %d: malloc fail, err[%s]", __FUNC__, __LINE__, nd_last_error());
				mamager->unlock(mamager, sessionid) ;
				return (-1);
			}
			memcpy(user, &data->user_info, sizeof(user_info_t));
			quadbox_t box = {user->pos_x, user->pos_y, user->pos_x, user->pos_y};

			nd_mutex_lock(map_lock);
			if (quadtree_insert(mapobj_tree, &user->quad_lst, &box) == NULL) {
				LOG_ERROR(mycat, "%s %d: quadtree_insert fail, err[%s]", __FUNC__, __LINE__, nd_last_error());
				free(user);
				nd_mutex_unlock(map_lock);				
				mamager->unlock(mamager, sessionid) ;
				return (-1);				
			}
			user->quad_head = user->quad_lst.prev;
			nd_mutex_unlock(map_lock);
			
				//key = calc_hashnr(user->rolename, strlen(user->rolename));

			nd_wrlock(name_map_lock);	
			username_map = rb_insert_v2(sizeof(user->rolename), user->rolename, username_map);
			nd_wrlock(name_map_lock);
	
			nd_wrlock(id_map_lock);
			userid_map = rb_insert(user->id, user, userid_map);			
			nd_wrunlock(id_map_lock);
	
			ND_USERMSG_MAXID(msg) = MAXID_SERVER_SERVER ;
			ND_USERMSG_MINID(msg) = MSG_USER_CHG_MAP_RET ;
			ND_USERMSG_PARAM(msg) = user->id;	
			ND_USERMSG_LEN(msg) = ND_USERMSG_HDRLEN;
			ret = nd_sessionmsg_sendex((nd_handle)session, (nd_usermsghdr_t *)msg, ESF_URGENCY);
		} else {
			LOG_ERROR(mycat, "%s %d: session is not valid sessionid[%d]", __FUNC__, __LINE__, sessionid);			
		}
		mamager->unlock(mamager, sessionid) ;
	} else {
		LOG_ERROR(mycat, "%s %d: can not find session by sessionid[%d]", __FUNC__, __LINE__, sessionid);			
	}
	return (0);
}

static NDINT32 send_to_all_gateserver(nd_usermsghdr_t * buf, nd_handle listener)
{
	NDINT32 ret;
	NDINT32 i;
	struct nd_client_map *session = NULL;	
	struct cm_manager *mamager = nd_listensrv_get_cmmamager(listener);
	nd_assert(mamager) ;

	for (i = 0; i < MAX_GATE_SERVER; ++i) {
		if (gate_server_id[i] == (NDUINT16)-1)
			continue;
		session = (struct nd_client_map *)mamager->lock(mamager, gate_server_id[i]) ;
		if(session) {
			if(nd_connector_valid((nd_netui_handle) session)
				&& gate_server_id[i] == session->connect_node.session_id) {
				ret = nd_sessionmsg_sendex((nd_handle)session, buf, ESF_URGENCY);
			} else {
				LOG_ERROR(mycat, "%s %d: session is not valid", __FUNC__, __LINE__);			
			}
			mamager->unlock(mamager, gate_server_id[i]) ;
		} else {
			LOG_ERROR(mycat, "%s %d: can not find session", __FUNC__, __LINE__);			
		}
	}
	return (0);
}
static NDINT32 user_scene_req(NDUINT16 sessionid, nd_usermsgbuf_t *msg , nd_handle listener)
{
	NDUINT32 id;
	user_info_t *user_info = NULL;
	rb_node_t *usernode;
	struct list_head *retlist[MAX_QUAD_SEARCH];
	struct list_head *pos;
	nd_usermsgbuf_t buf;// = ND_USERMSG_INITILIZER;
	user_scene_t *ack = (user_scene_t *)buf.data;
	struct nd_client_map *session = NULL;
	struct cm_manager *mamager = NULL;
	NDINT32 ret;
	NDINT32 index;
	NDINT32 i;
	
	assert(ND_USERMSG_MAXID(msg) == MAXID_SERVER_SERVER);
	assert(ND_USERMSG_MINID(msg) == MSG_USER_SCENE_REQ);

	id = ND_USERMSG_PARAM(msg);	
	nd_rdlock(id_map_lock);
	usernode = rb_search(id, userid_map);
	nd_rdunlock(id_map_lock);	
	if (!usernode || !usernode->data) {
		LOG_ERROR(mycat, "%s %d: can not find usernode[%d]", __FUNC__, __LINE__, id);					
		return (-1);
	}
	user_info = usernode->data;
	user_info->rollcall = 0;
	quadbox_t box = {user_info->pos_x - 100,
					 user_info->pos_y - 100,
					 user_info->pos_x + 100,
					 user_info->pos_y + 100};			
	index = 0;
	nd_mutex_lock(map_lock);
	quadtree_search(mapobj_tree, &box, retlist, &index, MAX_QUAD_SEARCH);
	ack->num = 0;
	for (i = 0; i < index; ++i) {
		list_for_each(pos, retlist[i]) {
			user_info = list_entry(pos, user_info_t, quad_lst);
			memcpy(&ack->player[ack->num].action, &user_info->act, sizeof(act_t));
			memcpy(ack->player[ack->num].rolename, user_info->rolename, NORMAL_SHORT_STR_LEN);
			ack->player[ack->num].x = user_info->pos_x;
			ack->player[ack->num].y = user_info->pos_y;			
			++ack->num;
		}
	}
	nd_mutex_unlock(map_lock);	

	mamager = nd_listensrv_get_cmmamager(listener);
	
	nd_assert(mamager) ;

	session = (struct nd_client_map *)mamager->lock(mamager, sessionid) ;
	if(session) {
		if(nd_connector_valid((nd_netui_handle) session) && sessionid == session->connect_node.session_id) {
			ND_USERMSG_MAXID(&buf) = MAXID_USER_SERVER ;
			ND_USERMSG_MINID(&buf) = MSG_USER_SCENE_ACK ;
			ND_USERMSG_LEN(&buf) = ND_USERMSG_HDRLEN + (sizeof(user_scene_t) + ack->num * sizeof(user_action_t));
			ND_USERMSG_PARAM(&buf) = id;	
			ND_USERMSG_ENCRYPT(&buf) = 0;
			ret = nd_sessionmsg_sendex((nd_handle)session, (nd_usermsghdr_t *)&buf, ESF_URGENCY);
		} else {
			LOG_ERROR(mycat, "%s %d: session is not valid sessionid[%d]", __FUNC__, __LINE__, sessionid);			
		}
		mamager->unlock(mamager, sessionid) ;
	} else {
		LOG_ERROR(mycat, "%s %d: can not find session by sessionid[%d]", __FUNC__, __LINE__, sessionid);			
	}
	return (0);
}
static NDINT32 user_action_req(NDUINT16 sessionid, nd_usermsgbuf_t *msg , nd_handle listener)
{
	user_action_t *data = (user_action_t *)msg->data;
	nd_usermsgbuf_t buf;// = ND_USERMSG_INITILIZER;
	user_action_t *ack = (user_action_t *)buf.data;
	broadcast_info_t *broadcast_data = (broadcast_info_t *)(buf.data + sizeof(user_action_t));
	NDINT32 ret = -1;
	NDUINT32 id;
	user_info_t *user_info = NULL;
	rb_node_t *usernode;
	NDINT32 usernum;
	struct list_head *retlist[MAX_QUAD_SEARCH];
	struct list_head *pos;
	NDINT32 index;
	NDINT32 i;
	
	assert(ND_USERMSG_MAXID(msg) == MAXID_SERVER_SERVER);
	assert(ND_USERMSG_MINID(msg) == MSG_USER_ACTION_REQ);
	assert(ND_USERMSG_DATALEN(msg) == sizeof(user_action_t));
	;//todo check data valid

		//get id from param
	id = ND_USERMSG_PARAM(msg);
		//find user_info by id
	nd_rdlock(id_map_lock);
	usernode = rb_search(id, userid_map);
	nd_rdunlock(id_map_lock);	
	if (!usernode || !usernode->data) {
		LOG_ERROR(mycat, "%s %d: can not find usernode[%d]", __FUNC__, __LINE__, id);							
		goto notfound;
	}
	user_info = usernode->data;
	user_info->rollcall = 0;
	if (strncmp(user_info->rolename, data->rolename, sizeof(data->rolename)) != 0) {
		LOG_ERROR(mycat, "%s %d: rolename not compare", __FUNC__, __LINE__);							
		goto notfound;
	}

		//list_del from quadtree
	nd_mutex_lock(map_lock);	
	list_del(&user_info->quad_lst);
	nd_mutex_unlock(map_lock);	

		//quadinsert into quadtree
	user_info->pos_x = data->x;
	user_info->pos_y = data->y;
	memcpy(&user_info->act, data, sizeof(user_info->act));
	quadbox_t box = {user_info->pos_x,
					 user_info->pos_y,
					 user_info->pos_x,
					 user_info->pos_y};			
	nd_mutex_lock(map_lock);
	if (quadtree_insert(mapobj_tree, &user_info->quad_lst, &box) == NULL) {
		nd_mutex_unlock(map_lock);
		LOG_ERROR(mycat, "%s %d: quadtree_insert fail, err[%s]", __FUNC__, __LINE__, nd_last_error());
		goto notfound;
	}
	user_info->quad_head = user_info->quad_lst.prev;			
	nd_mutex_unlock(map_lock);
	
	box._xmin = user_info->pos_x - 100;
	box._ymin = user_info->pos_y - 100;
	box._xmax = user_info->pos_x + 100;
	box._ymax = user_info->pos_y + 100;

	index = 0;
	nd_mutex_lock(map_lock);
	quadtree_search(mapobj_tree, &box, retlist, &index, MAX_QUAD_SEARCH);
	usernum = 0;

	for (i = 0; i < index; ++i) {	
		list_for_each(pos, retlist[i]) {
			user_info = list_entry(pos, user_info_t, quad_lst);
			if (user_info->id == id)
				continue;
			broadcast_data->userid[usernum] = user_info->id;
			++usernum;
		}
	}
	nd_mutex_unlock(map_lock);

	broadcast_data->usernum = usernum;
	broadcast_data->real_maxid = MAXID_USER_SERVER;
	broadcast_data->real_minid = MSG_USER_ACTION_ACK;
	ND_USERMSG_MAXID(&buf) = MAXID_SERVER_SERVER;
	ND_USERMSG_MINID(&buf) = MSG_BROADCAST_TOUSER;
	ND_USERMSG_PARAM(&buf) = sizeof(user_action_t);
	ND_USERMSG_LEN(&buf) = ND_USERMSG_HDRLEN + sizeof(user_action_t)
		+ sizeof(broadcast_info_t)
		+ sizeof(int) * broadcast_data->usernum;
	ND_USERMSG_ENCRYPT(&buf) = 0;
	memcpy(ack, data, sizeof(*ack));

	send_to_all_gateserver((nd_usermsghdr_t *)&buf, listener);
notfound:
	return (0);
}

static void show_userinfo(data_t data, void *param)
{
	user_info_t *user = (user_info_t *)data;
	ndprintf(_NDT("id[%d] rolename[%s] rollcall[%d] pos[%d:%d]\n"), user->id, user->rolename, user->rollcall, user->pos_x, user->pos_y);
}

static void show_quad_userinfo(struct list_head *head, void *param)
{
	struct list_head *pos;
	user_info_t *user = NULL;	
	list_for_each(pos, head) {
		user = list_entry(pos, user_info_t, quad_lst);
		ndprintf(_NDT("id[%d] rolename[%s] rollcall[%d] pos[%d:%d]\n"),
			user->id, user->rolename, user->rollcall, user->pos_x, user->pos_y);
	}
}

int main(int argc, char *argv[])
{
    char ch;
    int ret;
    nd_handle listen_handle;
	int i;
	
	for (i = 0; i < MAX_GATE_SERVER; ++i)
		gate_server_id[i] = -1;
	
	log4c_init();
	mycat = log4c_category_get("noviceserver");	

//    const log4c_location_info_t locinfo = LOG4C_LOCATION_INFO_INITIALIZER(NULL);	
//	log4c_category_log(mycat, LOG4C_PRIORITY_DEBUG, "Debugging app 1 - loop %d", i);
//	log4c_category_log_locinfo(mycat, &locinfo, LOG4C_PRIORITY_DEBUG, "Debugging app 1 - loop %d", i);
	LOG_DEBUG(mycat, "Debugging app 1 - loop %d", 10);
	
    ret = init_module();

	lua_init("config.lua");
	init_map("1.map");
	init_mapboj_tree();
	nd_rwlock_init(&name_map_lock);
	nd_rwlock_init(&id_map_lock);	
	ret = init_module();
//    ret = create_rsa_key();

	listen_handle = nd_object_create("listen-tcp") ;

	nd_set_connection_timeout(listen_handle, 60 * 60);
	
	nd_listensrv_set_single_thread(listen_handle) ;
    ret = nd_listensrv_session_info(listen_handle, get_maxclient(), 0);

	ND_SET_ONCONNECT_ENTRY(listen_handle, test_accept, NULL, test_deaccept) ;
	nd_srv_msgtable_create(listen_handle, MAXID_SYS + 1, 0);
	ND_INSTALL_HANDLER(listen_handle, test_usermsg_func, MAXID_SYS, SYM_ECHO, 0);

	ND_INSTALL_HANDLER(listen_handle, user_change_map, MAXID_SERVER_SERVER, MSG_USER_CHG_MAP, 0);
	ND_INSTALL_HANDLER(listen_handle, user_leave_map, MAXID_SERVER_SERVER, MSG_USER_LEAVE_MAP, 0);		
//	ND_INSTALL_HANDLER(listen_handle, user_move_req, MAXID_USER_SERVER, MSG_USER_MOVE_REQ, 0);
	ND_INSTALL_HANDLER(listen_handle, user_action_req, MAXID_SERVER_SERVER, MSG_USER_ACTION_REQ, 0);
	ND_INSTALL_HANDLER(listen_handle, user_scene_req, MAXID_SERVER_SERVER, MSG_USER_SCENE_REQ, 0);

	ND_INSTALL_HANDLER(listen_handle, server_reg_func, MAXID_SERVER_SERVER, MSG_SERVER_REG, 0);
	
	ret = nd_listensrv_open(get_listenport(), listen_handle);

	while (1) {
		if(kbhit()) {
			ch = getch() ;
			if(ND_ESC==ch){
				printf_dbg("you are hit ESC, program eixt\n") ;
				break ;
			}
			switch (ch)
			{
				case 'a':
					nd_rdlock(id_map_lock);
					rb_travel(userid_map, show_userinfo, NULL);
					nd_rdunlock(id_map_lock);					
					ndprintf(_NDT("==================\n"));
					
					nd_rdlock(name_map_lock);
					rb_travel(username_map, show_userinfo, NULL);
					nd_rdunlock(name_map_lock);
					ndprintf(_NDT("==================\n"));
					if (mapobj_tree) {
						nd_mutex_lock(map_lock);
						quad_travel(mapobj_tree->_root, show_quad_userinfo, NULL);
						nd_mutex_unlock(map_lock);
					}
					break;
				default:
					break;
			}
			
		}
		else {
			nd_sleep(1000) ;
		}
	}

//	ret = nd_listensrv_close(listen_handle, 0);	
	nd_srv_msgtable_destroy(listen_handle);

	nd_object_destroy(listen_handle, 0);

//	destroy_rsa_key();
	destroy_module();

	if (log4c_fini()){
		printf("log4c_fini() failed");
	}
	
    return 0;
}


